﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;

namespace Isuka.Iteration
{

    public static class Iterable
    {
        public static IIterable<StringIterator> AsIterable(this string source)
        {
            Contract.Requires<ArgumentNullException>(source != null, "source");
            Contract.Ensures(Contract.Result<IIterable<StringIterator>>() != null);

            return new Auxiliaries.AnonymousIterable<StringIterator>
            (
                () => new StringIterator(source),
                () => new StringIterator(source, source.Length)
            );
        }

        public static IIterable<ListIterator<T>> AsIterable<T>(this IList<T> source)
        {
            Contract.Requires<ArgumentNullException>(source != null, "source");
            Contract.Ensures(Contract.Result<IIterable<ListIterator<T>>>() != null);

            return new Auxiliaries.AnonymousIterable<ListIterator<T>>
            (
                () => new ListIterator<T>(source),
                () => new ListIterator<T>(source, source.Count)
            );
        }

        public static TIterator FindIterator<T, TIterator>(TIterator first, TIterator last, T value)
            where TIterator : class, IIterator<T>
        {
            Contract.Requires<ArgumentNullException>(first != null, "first");
            Contract.Requires<ArgumentNullException>(last != null, "last");

            return FindIterator(first, last, value, EqualityComparer<T>.Default.Equals);
        }

        public static TIterator FindIterator<T, TIterator>(TIterator first, TIterator last, T value, Func<T, T, bool> equalityComparer)
            where TIterator : class, IIterator<T>
        {
            Contract.Requires<ArgumentNullException>(first != null, "first");
            Contract.Requires<ArgumentNullException>(last != null, "last");
            Contract.Requires<ArgumentNullException>(equalityComparer != null, "equalityComparer");

            for (var i = first.Copy(); i.InRange && !i.Equals(last); i.MoveNext())
            {
                if (equalityComparer(i.Current, value))
                {
                    return i;
                }
            }
            return last.Copy();
        }

        /// <summary>
        /// 次の項目に移動。
        /// すでに末尾にあり、移動できない場合は false
        /// </summary>
        /// <param name="iter"></param>
        /// <param name="it"></param>
        /// <returns></returns>
        public static bool MoveNext(this IIterable<IIterator> iter, IIterator it)
        {
            Contract.Requires(iter != null);
            Contract.Requires<ArgumentNullException>(it != null, "it");

            var result = iter.InRange(it);
            if (result)
            {
                it.MoveNext();
            }
            return result;
        }

        public static bool InRange(this IIterable<IIterator> iter, IIterator it)
        {
            Contract.Requires<ArgumentNullException>(iter != null, "iter");
            Contract.Requires<ArgumentNullException>(it != null, "it");

            return it.InRange && !it.Equals(iter.End());
        }

        public static T[] ToArray<T>(IIterator<T> first, IIterator<T> last)
        {
            Contract.Requires<ArgumentNullException>(first != null, "first");
            Contract.Requires<ArgumentNullException>(last != null, "last");

            var dist = first.Distance(last);
            if (dist.HasValue)
            {
                var it = first.Copy();
                var array = new T[dist.Value];
                for (var i = 0; i < dist; i++, it.MoveNext())
                {
                    array[i] = it.Current;
                }
                return array;
            }
            else
            {
                return ToEnumerable(first, last).ToArray();
            }
        }

        public static IEnumerable<T> ToEnumerable<T>(this IIterable<IIterator<T>> source)
        {
            Contract.Requires<ArgumentNullException>(source != null, "source");
            Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);

            return ToEnumerableIterator(source.Begin(), source.End());
        }


        public static IEnumerable<T> ToEnumerable<T>(IIterator<T> first, IIterator<T> last)
        {
            Contract.Requires<ArgumentNullException>(first != null, "first");
            Contract.Requires<ArgumentNullException>(last != null, "last");

            return ToEnumerableIterator(first, last);
        }

        static IEnumerable<T> ToEnumerableIterator<T>(IIterator<T> first, IIterator<T> last)
        {
            for (var i = first.Copy(); i.InRange && !i.Equals(last); i.MoveNext())
            {
                yield return i.Current;
            }
        }

        public static string ToString(IIterator<char> first, IIterator<char> last)
        {
            Contract.Requires<ArgumentNullException>(first != null, "first");
            Contract.Requires<ArgumentNullException>(last != null, "last");

            var result = first.Distance(last) ?? 0;
            if (result < 0)
            {
                result = 0;
            }
            var builder = new StringBuilder(result);
            for (var i = first.Copy(); i.InRange && !i.Equals(last); i.MoveNext())
            {
                builder.Append(i.Current);
            }
            return builder.ToString();
        }
    }
}
